#include <math.h>
#include "iwdog.h"
#include "stm32l0xx_hal.h"
#include "board.h"

static IWDG_HandleTypeDef   IwdgHandle;
TIM_HandleTypeDef    Input_Handle;
__IO uint32_t uwCaptureNumber = 0;
__IO uint32_t uwPeriodValue = 0;
__IO uint32_t uwLsiFreq = 0;
uint16_t tmpCC4[2] = {0, 0};

/**
  * @brief  This function handles TIM21 global interrupt request.
  * @param  None
  * @retval None
  */
void TIM21_IRQHandler(void)
{
    HAL_TIM_IRQHandler(&Input_Handle);
}

/**
  * @brief  Input Capture callback in non blocking mode
  * @param  htim : TIM IC handle
  * @retval None
*/

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
    /* Get the Input Capture value */
    tmpCC4[uwCaptureNumber++] = HAL_TIM_ReadCapturedValue(&Input_Handle, TIM_CHANNEL_1);

    if(uwCaptureNumber >= 2)
    {
        /* Compute the period length */
        uwPeriodValue = (uint16_t)(0xFFFF - tmpCC4[0] + tmpCC4[1] + 1);

        /* Frequency computation */
        uwLsiFreq = (uint32_t) SystemCoreClock / uwPeriodValue;
        uwLsiFreq *= 8;
    }
}

/**
  * @brief TIM MSP Initialization
  *        This function configures the hardware resources used in this example:
  *           - Peripheral's clock enable
  *           - Peripheral's GPIO Configuration
  * @param htim: TIM handle pointer
  * @retval None
  */
void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim)
{

    /*## Enable peripherals and GPIO Clocks ####################################*/
    /* RCC LSI clock enable */
    __HAL_RCC_LSI_ENABLE();

    /* Wait till LSI is ready */
    while(__HAL_RCC_GET_FLAG(RCC_FLAG_LSIRDY) == RESET)
    {}

    /* TIMx Peripheral clock enable */
    __HAL_RCC_TIM21_CLK_ENABLE();

    /*## Configure the NVIC for TIMx ###########################################*/
    HAL_NVIC_SetPriority(TIM21_IRQn, 0, 0);

    /* Enable the TIM21 global Interrupt */
    HAL_NVIC_EnableIRQ(TIM21_IRQn);
}

/**
  * @brief  Configures TIM21 to measure the LSI oscillator frequency.
  * @param  None
  * @retval LSI Frequency
  */
static uint32_t GetLSIFrequency(void)
{
    TIM_IC_InitTypeDef    TIMInput_Config;

    /* Configure the TIM peripheral *********************************************/
    /* Set TIMx instance */
    Input_Handle.Instance = TIM21;

    /* TIM21 configuration: Input Capture mode ---------------------
    	 The LSI oscillator is connected to TIM21 CH1.
    	 The Rising edge is used as active edge.
    	 The TIM21 CCR1 is used to compute the frequency value.
    ------------------------------------------------------------ */
    Input_Handle.Init.Prescaler         = 0;
    Input_Handle.Init.CounterMode       = TIM_COUNTERMODE_UP;
    Input_Handle.Init.Period            = 0xFFFF;
    Input_Handle.Init.ClockDivision     = 0;
    if(HAL_TIM_IC_Init(&Input_Handle) != HAL_OK)
    {
        /* Initialization Error */
    }

    /* Connect internally the TIM21_CH1 Input Capture to the LSI clock output */
    HAL_TIMEx_RemapConfig(&Input_Handle, TIM21_TI1_LSI);

    /* Configure the Input Capture of channel 1 */
    TIMInput_Config.ICPolarity  = TIM_ICPOLARITY_RISING;
    TIMInput_Config.ICSelection = TIM_ICSELECTION_DIRECTTI;
    TIMInput_Config.ICPrescaler = TIM_ICPSC_DIV8;
    TIMInput_Config.ICFilter    = 0;
    if(HAL_TIM_IC_ConfigChannel(&Input_Handle, &TIMInput_Config, TIM_CHANNEL_1) != HAL_OK)
    {
        /* Initialization Error */
    }

    /* Start the TIM Input Capture measurement in interrupt mode */
    if(HAL_TIM_IC_Start_IT(&Input_Handle, TIM_CHANNEL_1) != HAL_OK)
    {
    }

    /* Wait until the TIM21 get 2 LSI edges */
    while(uwCaptureNumber != 2)
    {
    }

    /* Disable TIM21 CC1 Interrupt Request */
    HAL_TIM_IC_Stop_IT(&Input_Handle, TIM_CHANNEL_1);

    /* Deinitialize the TIM21 peripheral registers to their default reset values */
    HAL_TIM_IC_DeInit(&Input_Handle);

    return uwLsiFreq;
}

void iwdog_init()
{
    /*##-1- Check if the system has resumed from IWDG reset ####################*/
    if(__HAL_RCC_GET_FLAG(RCC_FLAG_IWDGRST) != RESET)
    {
        printf("###system reset by IWDG###\n");
        /* Clear reset flags */
        __HAL_RCC_CLEAR_RESET_FLAGS();
    }
    /*Counter Reload Value = 250ms/IWDG counter clock period
    												= 0.25s / (32/LsiFreq)
    												= LsiFreq/(32 * 4)
    												= LsiFreq/128 */

//	uwLsiFreq = 40000;		//40KHz,discreet value!!!
    uwLsiFreq = GetLSIFrequency();

    IwdgHandle.Instance = IWDG;
#ifndef LOW_POWER
    IwdgHandle.Init.Prescaler = IWDG_PRESCALER_32;
    IwdgHandle.Init.Reload = uwLsiFreq / 128;
#else
    IwdgHandle.Init.Prescaler = IWDG_PRESCALER_256;
    IwdgHandle.Init.Reload = 0xffff;
#endif
    IwdgHandle.Init.Window = IWDG_WINDOW_DISABLE;

    HAL_IWDG_Init(&IwdgHandle);
    HAL_IWDG_Start(&IwdgHandle);
}

void iwdog_refresh()
{
    HAL_IWDG_Refresh(&IwdgHandle);
}
